////////////////////////////////////////////////////////////////////////////////////////////////////
// file:	ADs.cs
//
// summary:	Implements a ds class
////////////////////////////////////////////////////////////////////////////////////////////////////

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.DirectoryServices;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Security.Principal;
using System.Data;
using Interop.ADSSECURITYLib_Strong;
using Dimok;
using Dimok.Exceptions;
//using Dimok.Data.ROOT.CIMV2;
using Dimok.Data;
using Microsoft.ApplicationBlocks.ExceptionManagement;
using ActiveDs;
using System.Diagnostics;

////////////////////////////////////////////////////////////////////////////////////////////////////
// namespace: Dimok.Data
//
// summary:	.
////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Dimok.Data {

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>	A ds. </summary>
	///
	/// <remarks>	Dimon, 12/21/2010. </remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////

	public class ADs{
		public class RIDException : Exception {

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Constructor. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="message">	The message. </param>
			/// <param name="exc">			The exc. </param>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public RIDException(string message, Exception exc) : base(message, exc) { }
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Exception for signalling create group errors. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public class CreateGroupException : Exception {

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Sets the container dn. </summary>
			///
			/// <value>	The container dn. </value>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public string ContainerDN { set { Data["ContainerDN"] = value; } }

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Constructor. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="groupName">		Name of the group. </param>
			/// <param name="containerDN">	The container dn. </param>
			/// <param name="exc">					The exc. </param>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public CreateGroupException(string groupName,string containerDN, Exception exc) : base() {
				Data["GroupName"] = groupName;
				Data["ContainerDN"] = containerDN;
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Directory entry ex. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public class DirectoryEntryEx : DirectoryEntry {

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Constructor. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="path">	Full pathname of the file. </param>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public DirectoryEntryEx(string path):base(GetFullShDN(path)){}
		}
		/// <summary> Name of a m account </summary>
		public const string sAMAccountName = "sAMAccountName";
		/// <summary> Name of the distinguished </summary>
		public const string DistinguishedName = "distinguishedName";
		/// <summary> The user account control </summary>
		public const string UserAccountControl = "userAccountControl";

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	 the " window nt. </summary>
		///
		/// <value>	The " window nt. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private const string WinNT = "WinNT://";

		#region NT Class
		public class NT {
			public static string GetFullShDN(string shDN) {
				return WinNT + Dns.GetHostName() + "/" + shDN;
			}

			#region Group
			public static DirectoryEntry GetGroup(string groupName) {
				return new DirectoryEntry(groupName + ",Group");
			}
			public static DirectoryEntry FindGroup(string groupName) {
				try {
					DirectoryEntry computerEntry = new DirectoryEntry(WinNT + Environment.MachineName);
					return computerEntry.Children.Find(groupName, "group");
				} catch { return null; }
			}
			public static DirectoryEntry CreateGroup(string groupName) {
				try {
					DirectoryEntry groupEntry = FindGroup(groupName);
					if (groupEntry != null) return groupEntry;
					DirectoryEntry computerEntry = new DirectoryEntry(WinNT + Environment.MachineName);
					groupEntry = computerEntry.Children.Add(groupName, "group");
					//groupEntry.Properties["Description"].Add("Test group");
					groupEntry.CommitChanges();
					return groupEntry;
				} catch (Exception exc) {
					throw new ApplicationException("GroupName:" + groupName, exc);
				}
			}

			#endregion

			#region User
			public static DirectoryEntry GetUser(string userName) {
				string domain = UserDomain(userName);
				string computer = Dns.GetHostName();
				DirectoryEntry de = new DirectoryEntry(WinNT + computer).Parent;
				return de.Name == domain ?
					new DirectoryEntry(de.Path + "/" + UserName(userName) + ",User", domain + "\\" + Registry.netUser, Registry.netPass, AuthenticationTypes.Secure)
					:
					new DirectoryEntry(de.Path + "/" + userName.Replace('\\', '/') + ",User");
			}
			public static DirectoryEntry FindUser(string userName) {
				try {
					DirectoryEntry computerEntry = new DirectoryEntry(WinNT + Environment.MachineName);
					return computerEntry.Children.Find(userName, "user");
				} catch { return null; }
			}
			public static DirectoryEntry CreateUser(string userName, string password, params DirectoryEntry[] groups) {
				//Dim AD As DirectoryEntry = _
				// New DirectoryEntry("WinNT://" + Environment.MachineName + ",computer")
				//Dim NewUser As DirectoryEntry = AD.Children.Add("TestUser1", "user")
				//NewUser.Invoke("SetPassword", New Object() {"#12345Abc"})
				//NewUser.Invoke("Put", New Object() {"Description", "Test User from .NET"})
				//NewUser.CommitChanges()
				//Dim grp As DirectoryEntry

				//grp = AD.Children.Find("Guests", "group")
				//If grp.Name <> "" Then
				//    grp.Invoke("Add", New Object() {NewUser.Path.ToString()})
				//End If

				DirectoryEntry computerEntry = new DirectoryEntry(WinNT + Environment.MachineName + ",computer");
				DirectoryEntry userEntry = FindUser(userName);
				if (userEntry == null) {
					userEntry = computerEntry.Children.Add(userName, "user");
					userEntry.Invoke("SetPassword", new object[] { password });
					userEntry.Properties["Description"].Add(password);
					userEntry.Properties["userFlags"].Add((int)ADS_USER_FLAG.ADS_UF_DONT_EXPIRE_PASSWD);
					userEntry.CommitChanges();
				}
				foreach (DirectoryEntry group in groups)
					try {
						group.Invoke("Add", new object[] { userEntry.Path });
					} catch (System.Reflection.TargetInvocationException exc) {
						COMException comExc = exc.InnerException as COMException;
						if (comExc == null || comExc.ErrorCode != -2147023518)
							throw new Exception("Group:" + group, exc);
					}
				return userEntry;
			}

			public static string[] GetUserGroups(DirectoryEntry user) { return GetUserGroups(user, null); }
			public static string[] GetUserGroups(DirectoryEntry user, string filter) {
				IADsUser adsUser = (IADsUser)user.NativeObject;
				IADsMembers groups = adsUser.Groups();
				ArrayListEx alGroups = new ArrayListEx();
				foreach (object group in groups)
					if (filter == null || Regex.IsMatch(((IADsGroup)group).Name, filter, RegexOptions.IgnoreCase))
						alGroups.Add(((IADsGroup)group).Name);
				return alGroups;
			}

			#endregion
		}
		#endregion

		#region LDAP Class

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Ldap. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public class LDAP {

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Gets a context for the domain. </summary>
			///
			/// <value>	The domain context. </value>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static string domainContext {
				get { return new DirectoryEntry("LDAP://RootDSE").Invoke("Get", "rootDomainNamingContext").ToString(); }
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Gets a full sh dn. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="shDN">	The sh dn. </param>
			///
			/// <returns>	The full sh dn. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static string GetFullShDN(string shDN) {
        if (IsAbsoluteShDN(shDN)) return shDN;
        shDN = Regex.Replace(shDN, ",?" + domainContext, "", RegexOptions.IgnoreCase | RegexOptions.Compiled);
        if (shDN != "") shDN += ",";
        return "LDAP://" + shDN + domainContext;
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Gets the name of the domain. </summary>
			///
			/// <value>	The name of the domain. </value>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static string DomainName { 
				get {
					List<string> lst = new List<string>();
					foreach (string dc in new DirectoryEntry().Properties[DistinguishedName].Value.ToString().Split(','))
						lst.Add(dc.Split('=')[1]);
					return string.Join(".",lst.ToArray());
				}
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Query if 'shDN' is absolute sh dn. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <exception cref="ApplicationException">					Thrown when application. </exception>
			/// <exception cref="Exception">										Thrown when exception. </exception>
			/// <exception cref="UserExistsException">					Thrown when userexists. </exception>
			/// <exception cref="ASPException">									Thrown when asp. </exception>
			/// <exception cref="CreateGroupException">					Thrown when creategroup. </exception>
			/// <exception cref="ClientException">							Thrown when client. </exception>
			/// <exception cref="ObjectDoesNotExistException">	Thrown when objectdoesnotexist. </exception>
			/// <exception cref="ObjectExistsException">				Thrown when objectexists. </exception>
			///
			/// <param name="shDN">	The sh dn. </param>
			///
			/// <returns>	true if absolute sh dn, false if not. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static bool IsAbsoluteShDN(string shDN) { return shDN.IndexOf("://") > 0; }

			#region User

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Searches for the first users by property. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="propertyName">	Name of the property. </param>
			/// <param name="filter">				The filter. </param>
			///
			/// <returns>	The found users by property. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry[] FindUsersByProperty(string propertyName, string filter) {
				DirectorySearcher ds = new DirectorySearcher(new DirectoryEntry("LDAP://" + domainContext), "(&(objectCategory=person)(objectClass=user)(" + propertyName + "=" + filter + "))");
				ArrayList alUsers = new ArrayList();
				foreach (SearchResult sr in ds.FindAll()) {
					DirectoryEntry de = sr.GetDirectoryEntry();
					//if(  de.Properties[propertyName].Value != null && Regex.IsMatch(de.Properties[propertyName].Value.ToString(),filter,RegexOptions.IgnoreCase) )
					alUsers.Add(de);
				}
				return (DirectoryEntry[])alUsers.ToArray(typeof(DirectoryEntry));
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Gets a user. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <exception cref="ApplicationException">	Thrown when application. </exception>
			/// <exception cref="Exception">						Thrown when exception. </exception>
			///
			/// <param name="userName">			Name of the user. </param>
			/// <param name="objectClass">	The object class. </param>
			///
			/// <returns>	The user. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry GetUser(string userName,string objectClass) {
				userName = UserName(userName);
				string search = objectClass != "" ? 
					"(&(objectClass=" + objectClass + ")(" + sAMAccountName + "=" + userName + "))" : "(" + sAMAccountName + "=" + userName + ")";
				try {
					DirectorySearcher ds = new DirectorySearcher(search);
					SearchResult sr = ds.FindOne();
					if (sr == null) throw new ApplicationException("Can't find \"" + userName + "\" in Active Directory.");
					return sr.GetDirectoryEntry();
					//return new DirectoryEntry(sr.GetDirectoryEntry().Path,Registry.netUser,Registry.netPass);
				} catch (Exception exp) {
					exp.Data["User Name"] = userName;
					throw;
				}
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	This is the newest one. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="userDN">	The user dn. </param>
			///
			/// <returns>	The user groups ds. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static ADsGroupDS GetUserGroupsDS(string userDN) {
				return GetUserGroupsDS(new DirectoryEntry(GetFullShDN(userDN)));
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	This is the newest one. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="userDN">		The user dn. </param>
			/// <param name="groupsDN">	The groups dn. </param>
			/// <param name="drUser">		The dr user. </param>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static void GetUserGroupsDS(string userDN, string groupsDN, ADsUser.th_AdsUserRow drUser) {
				ADsGroupDS.t_GroupDataTable dtGroup = GetUserGroupsDS(new DirectoryEntryEx(userDN),groupsDN).t_Group;
				foreach (ADsGroupDS.t_GroupRow drGroup in dtGroup)
					((ADsUser)drUser.Table.DataSet).AdsGroup.AddAdsGroupRow(drUser, drGroup.sam, drGroup.cn, drGroup.dn, drGroup.sid);
			}
			//public static ADsGroupDS GetUserGroupsDS(string userDN, string OrderBy) {
			//  ADsGroupDS ds = GetUserGroupsDS(new DirectoryEntry(GetFullShDN(userDN)));
			//  if(OrderBy+"" == "" )return ds;
			//  ADsGroupDS dsSorted = new ADsGroupDS();
			//  dsSorted.Merge(ds.t_Group.Select("", OrderBy));
			//  return dsSorted;
			//}
			/// <summary>
			/// This is the newest one
			/// </summary>
			/// <param name="user"></param>
			/// <returns></returns>

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Gets a user groups ds. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="user">	Name of the user. </param>
			///
			/// <returns>	The user groups ds. </returns>
			///
			/// ### <param name="groupShDN">	group RDN. </param>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static ADsGroupDS GetUserGroupsDS(DirectoryEntry user) { return GetUserGroupsDS(user, ""); }

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	This is the newest one. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="user">			Name of the user. </param>
			/// <param name="GroupsDN">	The groups dn. </param>
			///
			/// <returns>	The user groups ds. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static ADsGroupDS GetUserGroupsDS(DirectoryEntry user,string GroupsDN) {
				return Props.MemberOf_DN(user,GroupsDN);
			}

      ////////////////////////////////////////////////////////////////////////////////////////////////////
      /// <summary>	Creates a user. </summary>
      ///
      /// <remarks>	Dimon, 12/21/2010. </remarks>
      ///
      /// <param name="containerShDN">					The container sh dn. </param>
      /// <param name="userName">								Name of the user. </param>
      /// <param name="password">								The password. </param>
      /// <param name="properties">							The properties. </param>
      /// <param name="groupShDN">							group RDN. </param>
      /// <param name="removeFromDomainUsers">	true to remove from domain users. </param>
      /// <param name="updateIfExists">					true to update if exists. </param>
      ///
      /// <returns>	. </returns>
      ////////////////////////////////////////////////////////////////////////////////////////////////////

      public static DirectoryEntry CreateUser(string containerShDN, string userName, string password, StringDictionary properties, string groupShDN, bool removeFromDomainUsers, bool updateIfExists) {
				return CreateUser(containerShDN, userName, password, properties, new string[] { groupShDN }, removeFromDomainUsers,updateIfExists);
			}

      ////////////////////////////////////////////////////////////////////////////////////////////////////
      /// <summary>	Creates a user. </summary>
      ///
      /// <remarks>	Dimon, 12/21/2010. </remarks>
      ///
      /// <param name="containerShDN">					The container sh dn. </param>
      /// <param name="userName">								Name of the user. </param>
      /// <param name="password">								The password. </param>
      /// <param name="properties">							The properties. </param>
      /// <param name="groupShDNs">							The group sh d ns. </param>
      /// <param name="removeFromDomainUsers">	true to remove from domain users. </param>
      /// <param name="updateIfExists">					true to update if exists. </param>
      ///
      /// <returns>	. </returns>
      ////////////////////////////////////////////////////////////////////////////////////////////////////

      public static DirectoryEntry CreateUser(string containerShDN, string userName, string password, StringDictionary properties, string[] groupShDNs, bool removeFromDomainUsers, bool updateIfExists) {
				DirectoryEntry ent = new DirectoryEntry();
				DirectoryEntry ou = IsAbsoluteShDN(containerShDN) ? new DirectoryEntry(containerShDN) : ent.Children.Find(containerShDN);
				return CreateUser(ou, userName, password, properties, groupShDNs, removeFromDomainUsers,updateIfExists);
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Creates a user. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <exception cref="ApplicationException">	Thrown when application. </exception>
			/// <exception cref="UserExistsException">	Thrown when userexists. </exception>
			/// <exception cref="Exception">						Thrown when exception. </exception>
			///
			/// <param name="deParent">								The de parent. </param>
			/// <param name="userName">								Name of the user. </param>
			/// <param name="password">								The password. </param>
			/// <param name="properties">							The properties. </param>
			/// <param name="groupShDNs">							The group sh d ns. </param>
			/// <param name="removeFromDomainUsers">	true to remove from domain users. </param>
			/// <param name="updateIfExists">					true to update if exists. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry CreateUser(DirectoryEntry deParent, string userName, string password, StringDictionary properties, string[] groupShDNs,/* string adminName,string adminPass,*/bool removeFromDomainUsers,bool updateIfExists) {
				const int ADS_USER_FLAGS = (int)ADS_USER_FLAG.ADS_UF_DONT_EXPIRE_PASSWD | (int)ADS_USER_FLAG.ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED;
				if (properties == null) properties = new StringDictionary();
				userName = userName.Trim();
				password = password.Trim();
				if (userName == "") throw new ApplicationException("UserName must be provided in order to create an Account.");
				string fullUserName = userName + "@" + DomainName;
				DirectorySearcher src = new DirectorySearcher(new DirectoryEntry(), "(&(userPrincipalName=" + fullUserName + ")(objectCategory=person))");
        DirectoryEntry usr = null;
        SearchResult sr = src.FindOne();
        if( sr != null)usr = sr.GetDirectoryEntry();
				if ( !updateIfExists && usr != null) throw new UserExistsException(userName);
				string fullName = properties.ContainsKey("givenName") && properties.ContainsKey("sn")
					? properties["givenName"] + " " + properties["sn"] : userName;
				fullName = fullName.Trim() == "" ? userName : fullName;
        if (usr == null) {
					if (password == "") throw new ApplicationException("Password must be provided in order to create an Account.");
					//usr.Username = adminName;	usr.Password = adminPass;
          usr = deParent.Children.Add("CN=" + fullName, "user");
          usr.Properties[sAMAccountName].Value = userName;
          usr.Properties["userPrincipalName"].Value = fullUserName;
          usr.CommitChanges();
        }
        //usr.Username = adminName;	usr.Password = adminPass;
        usr.Properties[UserAccountControl].Value = ((int)usr.Properties[UserAccountControl].Value | ADS_USER_FLAGS) & ~(int)ADS_USER_FLAG.ADS_UF_ACCOUNTDISABLE;
				usr.CommitChanges();
				if ((password+"") != "") usr.Invoke("SetPassword", new object[] { password });
        //usr.Invoke("ChangePassword", new object[] { "", password });
				//usr.UsePropertyCache = false;
				foreach (DictionaryEntry de in properties)
					try {
						usr.Properties[de.Key.ToString()].Value = de.Value;
					} catch (Exception exc) { throw new Exception("\nProperty:" + de.Key.ToString() + "\n", exc); }
				usr.CommitChanges();
				for (int i = 0; i < groupShDNs.Length; i++)
					AddMemberToGroup(usr, groupShDNs[i], i == 0);
				if (removeFromDomainUsers) RemoveMemberFromGroup(usr, "CN=Domain Users,CN=Users");
				Props.MemberOfCache.Remove(usr.Path);
				return usr;
			}

			#endregion

			#region Object

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Gets an object. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="propValue">	The property value. </param>
			///
			/// <returns>	The object. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry GetObject(string propValue) { return GetObject(propValue, "cn"); }

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Gets an object. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <exception cref="ApplicationException">	Thrown when application. </exception>
			/// <exception cref="ASPException">					Thrown when asp. </exception>
			///
			/// <param name="propValue">	The property value. </param>
			/// <param name="propName">		Name of the property. </param>
			///
			/// <returns>	The object. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry GetObject(string propValue, string propName) {
				string adsPath = "";
				try {
					adsPath = "LDAP://" + domainContext;
					DirectoryEntry de = new DirectoryEntry(adsPath);
					DirectorySearcher ds = new DirectorySearcher(de, "(" + propName + "=" + propValue + ")");
					SearchResult sr = ds.FindOne();
					if (sr == null) throw new ApplicationException("Can't find object \"" + propName + "=" + propValue + "\" in Active Directory.");
					return new DirectoryEntry(sr.GetDirectoryEntry().Path);
				} catch (Exception exp) {
					throw new ASPException("adsPath:" + adsPath, exp);
				}
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Searches for the first object. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="containerDN">	The container dn. </param>
			/// <param name="objectClass">	The object class. </param>
			///
			/// <returns>	The found object. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			private static DirectoryEntry[] FindObject(string containerDN, string objectClass) {
				return FindObject(new DirectoryEntryEx(containerDN), objectClass);
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Searches for the first object. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="deContainer">	The de container. </param>
			/// <param name="objectClass">	The object class. </param>
			///
			/// <returns>	The found object. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			private static DirectoryEntry[] FindObject(DirectoryEntry deContainer, string objectClass) {
				return FindObject(deContainer, objectClass, SearchScope.Subtree);
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Searches for the first object. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="deContainer">	The de container. </param>
			/// <param name="objectClass">	The object class. </param>
			/// <param name="scope">				The scope. </param>
			///
			/// <returns>	The found object. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			private static DirectoryEntry[] FindObject(DirectoryEntry deContainer, string objectClass, SearchScope scope) {
				DirectorySearcher ds = new DirectorySearcher(deContainer, "(objectClass=" + objectClass + ")", null, scope);
				//ds.SearchScope = SearchScope.OneLevel;
				ArrayListDE alUsers = new ArrayListDE();
				foreach (SearchResult sr in ds.FindAll()) {
					alUsers.Add(sr.GetDirectoryEntry());
				}
				return alUsers;
			}
			#endregion

			#region Group
			#region Find

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Searches for the first group. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="parent">			The parent. </param>
			/// <param name="groupName">	Name of the group. </param>
			///
			/// <returns>	The found group. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry FindGroup(string parent, string groupName) {
        return FindGroup(new DirectoryEntryEx(parent), groupName,SearchScope.OneLevel);
      }

      ////////////////////////////////////////////////////////////////////////////////////////////////////
      /// <summary>	Searches for the first group. </summary>
      ///
      /// <remarks>	Dimon, 12/21/2010. </remarks>
      ///
      /// <param name="parent">			The parent. </param>
      /// <param name="groupName">	Name of the group. </param>
      /// <param name="scope">			The scope. </param>
      ///
      /// <returns>	The found group. </returns>
      ////////////////////////////////////////////////////////////////////////////////////////////////////

      public static DirectoryEntry FindGroup(string parent, string groupName, SearchScope scope) {
        return FindGroup(new DirectoryEntryEx(parent), groupName, scope);
      }

      ////////////////////////////////////////////////////////////////////////////////////////////////////
      /// <summary>	Searches for the first group. </summary>
      ///
      /// <remarks>	Dimon, 12/21/2010. </remarks>
      ///
      /// <param name="parent">			The parent. </param>
      /// <param name="groupName">	Name of the group. </param>
      ///
      /// <returns>	The found group. </returns>
      ////////////////////////////////////////////////////////////////////////////////////////////////////

      public static DirectoryEntry FindGroup(DirectoryEntry parent, string groupName) {
        return FindGroup(parent, groupName, "CN",SearchScope.OneLevel);
      }

      ////////////////////////////////////////////////////////////////////////////////////////////////////
      /// <summary>	Searches for the first group. </summary>
      ///
      /// <remarks>	Dimon, 12/21/2010. </remarks>
      ///
      /// <param name="parent">			The parent. </param>
      /// <param name="groupName">	Name of the group. </param>
      /// <param name="scope">			The scope. </param>
      ///
      /// <returns>	The found group. </returns>
      ////////////////////////////////////////////////////////////////////////////////////////////////////

      public static DirectoryEntry FindGroup(DirectoryEntry parent, string groupName,SearchScope scope) {
        return FindGroup(parent, groupName, "CN", scope);
      }

      ////////////////////////////////////////////////////////////////////////////////////////////////////
      /// <summary>	Searches for the first group. </summary>
      ///
      /// <remarks>	Dimon, 12/21/2010. </remarks>
      ///
      /// <param name="parent">			The parent. </param>
      /// <param name="propValue">	The property value. </param>
      /// <param name="propName">		Name of the property. </param>
      ///
      /// <returns>	The found group. </returns>
      ////////////////////////////////////////////////////////////////////////////////////////////////////

      public static DirectoryEntry FindGroup(string parent, string propValue, string propName) {
        return FindGroup(new DirectoryEntryEx(parent), propValue, propName,SearchScope.OneLevel);
      }

      ////////////////////////////////////////////////////////////////////////////////////////////////////
      /// <summary>	Searches for the first group. </summary>
      ///
      /// <remarks>	Dimon, 12/21/2010. </remarks>
      ///
      /// <param name="parent">			The parent. </param>
      /// <param name="propValue">	The property value. </param>
      /// <param name="propName">		Name of the property. </param>
      /// <param name="scope">			The scope. </param>
      ///
      /// <returns>	The found group. </returns>
      ////////////////////////////////////////////////////////////////////////////////////////////////////

      public static DirectoryEntry FindGroup(string parent, string propValue, string propName,SearchScope scope) {
        return FindGroup(new DirectoryEntryEx(parent), propValue, propName,scope);
      }

      ////////////////////////////////////////////////////////////////////////////////////////////////////
      /// <summary>	Searches for the first group. </summary>
      ///
      /// <remarks>	Dimon, 12/21/2010. </remarks>
      ///
      /// <param name="parent">			The parent. </param>
      /// <param name="propValue">	The property value. </param>
      /// <param name="propName">		Name of the property. </param>
      ///
      /// <returns>	The found group. </returns>
      ////////////////////////////////////////////////////////////////////////////////////////////////////

      public static DirectoryEntry FindGroup(DirectoryEntry parent, string propValue, string propName) {
        return FindGroup(parent, propName, propValue, SearchScope.OneLevel);
      }

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Searches for the first group. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="parent">			The parent. </param>
			/// <param name="propValue">	The property value. </param>
			/// <param name="propName">		Name of the property. </param>
			/// <param name="scope">			The scope. </param>
			///
			/// <returns>	The found group. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry FindGroup(DirectoryEntry parent, string propValue, string propName,SearchScope scope) {
        DirectorySearcher ds = new DirectorySearcher(parent, "(" + propName + "=" + propValue + ")", new string[] { },scope);
				SearchResult sr = ds.FindOne();
				return sr == null ? null : sr.GetDirectoryEntry();
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Searches for the first groups ds. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="ContainerDN">	The container dn. </param>
			/// <param name="SubTree">			true to sub tree. </param>
			///
			/// <returns>	The found groups ds. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static ADsGroupDS FindGroupsDS(string ContainerDN, bool SubTree) {
				return FindGroupsDS(ContainerDN, "", SubTree);
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Searches for the first groups ds. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="ContainerDN">		The container dn. </param>
			/// <param name="cnRegExFilter">	The cn register ex filter. </param>
			/// <param name="SubTree">				true to sub tree. </param>
			///
			/// <returns>	The found groups ds. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static ADsGroupDS FindGroupsDS(string ContainerDN, string cnRegExFilter, bool SubTree) {
				return FindGroupsDS(new DirectoryEntryEx(ContainerDN),cnRegExFilter, SubTree);
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Searches for the first groups ds. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="deContainer">	The de container. </param>
			/// <param name="SubTree">			true to sub tree. </param>
			///
			/// <returns>	The found groups ds. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static ADsGroupDS FindGroupsDS(DirectoryEntry deContainer, bool SubTree) {
				return FindGroupsDS(deContainer, "", SubTree);
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Searches for the first groups ds. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="deContainer">		The de container. </param>
			/// <param name="cnRegExFilter">	The cn register ex filter. </param>
			/// <param name="SubTree">				true to sub tree. </param>
			///
			/// <returns>	The found groups ds. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static ADsGroupDS FindGroupsDS(DirectoryEntry deContainer, string cnRegExFilter, bool SubTree) {
				DirectorySearcher ds = new DirectorySearcher(deContainer, "(objectClass=group)", new string[] { sAMAccountName, DistinguishedName,"cn","objectSID" }, SubTree?SearchScope.Subtree:SearchScope.OneLevel);
				ADsGroupDS.t_GroupDataTable dtGroup = new ADsGroupDS().t_Group;
				foreach (SearchResult sr in ds.FindAll())
					if ((cnRegExFilter + "") == "" || Regex.IsMatch(sr.Properties["cn"][0] + "", cnRegExFilter, RegexOptions.IgnoreCase))
						dtGroup.Addt_GroupRow(sr.Properties[sAMAccountName][0] + "", sr.Properties["cn"][0] + "", sr.Properties[DistinguishedName][0] + "", BuildOctetString((byte[])sr.Properties["objectSID"][0]));
				return (ADsGroupDS)dtGroup.DataSet;
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Searches for the first groups. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="containerDN">	The container dn. </param>
			///
			/// <returns>	The found groups. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry[] FindGroups(string containerDN) { return FindObject(containerDN, "group"); }

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Searches for the first groups. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="container">	The container. </param>
			///
			/// <returns>	The found groups. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry[] FindGroups(DirectoryEntry container) { return FindObject(container, "group"); }

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Gets the groups in ou. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="path">	Full pathname of the file. </param>
			///
			/// <returns>	The groups in ou. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntries GetGroupsInOU(string path) {
				DirectoryEntry de = new DirectoryEntry(GetFullShDN(path));
				//de.Children.SchemaFilter.Add("group");
				return de.Children;
			}
			#endregion

			#region Primary

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Gets a user primary group. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="userName">	Name of the user. </param>
			///
			/// <returns>	The user primary group. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry GetUserPrimaryGroup(string userName) { return GetUserPrimaryGroup(GetUser(userName,"user")); }

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Gets a user primary group. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <exception cref="Exception">	Thrown when exception. </exception>
			///
			/// <param name="user">	User Account object. </param>
			///
			/// <returns>	The user primary group. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry GetUserPrimaryGroup(DirectoryEntry user) {
				try {
          byte[] objectSid = user.Properties["objectSid"].Value as byte[];
          user.RefreshCache(new string[] { "primaryGroupId" });
          if (user.Properties.Contains("primaryGroupId")) {
            int pgi = (int)user.Properties["primaryGroupId"].Value;
            //calculate the primary group's SID
            byte[] primaryGroupSid = CreatePrimaryGroupSID(objectSid, pgi);
            //bind to the Primary Group
            DirectoryEntry dePG = new DirectoryEntry("LDAP://<SID=" + BuildOctetString(primaryGroupSid) + ">");
						return dePG;
          }
          ///////////////////////////////////////////////////////////////
					ADsSIDClass adsSID = new ADsSIDClass();
					adsSID.SetAs((int)ADS_SID_FORMAT.ADS_SID_ACTIVE_DIRECTORY_PATH, user.Path);
					string domainSID = adsSID.GetAs((int)ADS_SID_FORMAT.ADS_SID_SDDL).ToString();
					domainSID = Regex.Match(domainSID, "(.+-).+$").Groups[1].Value;
					user.Invoke("GetInfoEx", new object[] { "primaryGroupID" }, 0);
					int o = (Int32)user.Invoke("Get", "primaryGroupID");
					domainSID += (uint)o;
					adsSID.SetAs((int)ADS_SID_FORMAT.ADS_SID_SDDL, domainSID);
					string sidBind = adsSID.GetAs((int)ADS_SID_FORMAT.ADS_SID_HEXSTRING).ToString();
					sidBind = "LDAP://<SID=" + sidBind + ">";
					return new DirectoryEntry(sidBind);//,Registry.netUser,Registry.netPass,AuthenticationTypes.Delegation);
					// *** Working code *** Do not remove *** 
					//	adsSID.SetAs((int)ADS_SID_FORMAT.ADS_SID_RAW,o);
					//			adsSID.SetAs((int)ADS_SID_FORMAT.ADS_SID_RAW,user.Properties["primaryGroupID"][0]);
					// return adsSID.GetAs((int)ADS_SID_FORMAT.ADS_SID_SAM).ToString();
				} catch (Exception exp) { throw new Exception("UserPath:" + user.Path, exp); }
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Gets a user primary group sam. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="userName">	Name of the user. </param>
			///
			/// <returns>	The user primary group sam. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static string GetUserPrimaryGroupSAM(string userName) {
				string sAM = Props.SAMAccountName(GetUserPrimaryGroup(userName));
				return sAM;
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Gets a user primary group sam. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="user">	Name of the user. </param>
			///
			/// <returns>	The user primary group sam. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static string GetUserPrimaryGroupSAM(DirectoryEntry user) {
				string sAM = Props.SAMAccountName(GetUserPrimaryGroup(user));
				return sAM;
			}
      /// <summary>
      /// This method takes the RID of the Primary Group and overlays it
      /// on the last bytes of the user's SID byte array.  This maps to the 
      /// SID of the Primary Group for that user
      /// </summary>
      /// <param name="userSid"></param>
      /// <param name="primaryGroupID"></param>
      /// <returns></returns>
      private static byte[] CreatePrimaryGroupSID(byte[] userSid, int primaryGroupID) {
        //convert the int into a byte array
        byte[] rid = BitConverter.GetBytes(primaryGroupID);

        //place the bytes into the user's SID byte array
        //overwriting them as necessary
        for (int i = 0; i < rid.Length; i++) {
          userSid.SetValue(rid[i], new long[] { userSid.Length - (rid.Length - i) });
        }

        return userSid;
      }
      #endregion

			#region Create

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Creates a group. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <exception cref="CreateGroupException">	Thrown when creategroup. </exception>
			///
			/// <param name="groupName">	Name of the group. </param>
			/// <param name="pathShDN">		The path sh dn. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry CreateGroup(string groupName, string pathShDN) {
				try {
					return CreateGroup(groupName, new DirectoryEntry(GetFullShDN(pathShDN)));
				} catch (CreateGroupException exc) {
					exc.ContainerDN = pathShDN;
					throw exc;
				}
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Creates a group. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <exception cref="CreateGroupException">	Thrown when creategroup. </exception>
			///
			/// <param name="groupName">	Name of the group. </param>
			/// <param name="deParent">		The de parent. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry CreateGroup(string groupName, DirectoryEntry deParent) {
				try {
					deParent.RefreshCache();
					string commonName = Regex.Replace(groupName, @"\s*[{].+", "");
					DirectoryEntry objGroup = FindGroup(deParent, commonName);
					if (objGroup != null) return objGroup;
					objGroup = deParent.Children.Add("CN=" + commonName, "group");
					objGroup.Properties[sAMAccountName].Add(groupName);
					objGroup.Properties["displayName"].Add(commonName);
					objGroup.CommitChanges();
					return objGroup;
				} catch (Exception exc) {
					throw new CreateGroupException(groupName, "", exc);
				}
			}
			#endregion

			#endregion

			#region GroupUsers

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Searches for the first users. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="containerDN">	The container dn. </param>
			///
			/// <returns>	The found users. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry[] FindUsers(string containerDN) { return FindObject(containerDN, "user"); }

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Searches for the first users. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="container">	The container. </param>
			///
			/// <returns>	The found users. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry[] FindUsers(DirectoryEntry container) { return FindObject(container, "user"); }

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Searches for the first users ds. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="fillGroups">	Groups the fill belongs to. </param>
			/// <param name="groupsDN">		The groups dn. </param>
			/// <param name="SubTree">		true to sub tree. </param>
			///
			/// <returns>	The found users ds. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static ADsUser FindUsersDS(bool fillGroups, string groupsDN, bool SubTree) {
				return FindUsersDS(new DirectoryEntry(), fillGroups, groupsDN, SubTree);
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Searches for the first users ds. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="deContainer">	The de container. </param>
			/// <param name="fillGroups">		Groups the fill belongs to. </param>
			/// <param name="groupsDN">			The groups dn. </param>
			/// <param name="SubTree">			true to sub tree. </param>
			///
			/// <returns>	The found users ds. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static ADsUser FindUsersDS(DirectoryEntry deContainer, bool fillGroups, string groupsDN, bool SubTree) {
				DirectorySearcher ds = new DirectorySearcher(deContainer, "(&(objectClass=user)(objectCategory=person))", new string[] { sAMAccountName, DistinguishedName, "cn", "objectSID", UserAccountControl }, SubTree ? SearchScope.Subtree : SearchScope.OneLevel);
				ADsUser dsUser = new ADsUser();
        foreach (SearchResult sr in ds.FindAll()) {
          try {
            if (/*sr.Properties[UserAccountControl].Count > 0 &&*/ ((int)sr.Properties[UserAccountControl][0] & (int)ADS_USER_FLAG.ADS_UF_ACCOUNTDISABLE) == 0) {
              ADsUser.th_AdsUserRow drUser =
                  dsUser.th_AdsUser.Addth_AdsUserRow(
                  sr.Properties[DistinguishedName][0] + "", sr.Properties["cn"][0] + "", "", "", sr.Properties[sAMAccountName][0] + "");
              if (fillGroups) {
                GetUserGroupsDS(sr.Properties[DistinguishedName][0] + "", groupsDN, drUser);
                if (groupsDN != "" && drUser.GetAdsGroupRows().Length == 0) drUser.Delete();
              }
            }
          } catch {
            //Debugger.Break();
          }
        }
				dsUser.AcceptChanges();
				return dsUser;
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Searches for the first users ds. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="containerDN">	The container dn. </param>
			///
			/// <returns>	The found users ds. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static ADsUser FindUsersDS(string containerDN) {
				DirectoryEntry[] users = FindUsers(containerDN);
				ADsUser dsUsers = new ADsUser();
				foreach (DirectoryEntry user in users)
					dsUsers.th_AdsUser.Addth_AdsUserRow(user.Path, user.Name, "", "", Props.SAMAccountName(user));
				return dsUsers;
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Searches for the first users by groups. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="ContainerDN">	The container dn. </param>
			/// <param name="PullGroups">		Groups the pull belongs to. </param>
			///
			/// <returns>	The found users by groups. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static ADsUser FindUsersByGroups(string ContainerDN,bool PullGroups) {
				ADsUser dsUsers = new ADsUser();
				ADsUser.AdsGroupDataTable dtGroup = dsUsers.AdsGroup;
				ADsUser.th_AdsUserDataTable dtUser = dsUsers.th_AdsUser;
				foreach (DirectoryEntry deGroup in ADs.LDAP.FindGroups(ContainerDN))
					foreach (string memberPath in deGroup.Properties["member"]) {
						DirectoryEntry member = new DirectoryEntry("LDAP://" + memberPath);
						if (member.SchemaClassName == "user" && dtUser.FindByUserName(ADs.Props.sAM(member)) == null) {
							ADsUser.th_AdsUserRow drUser = dtUser.Addth_AdsUserRow(ADs.Props.DN(member), ADs.Props.CN(member), "", "", ADs.Props.sAM(member));
							if (!PullGroups) continue;
							foreach (ADsGroupDS.t_GroupRow drGroup in GetUserGroupsDS(member).t_Group) 
								dtGroup.AddAdsGroupRow(drUser,drGroup.sam,drGroup.cn,drGroup.dn,drGroup.sid);
						}
					}
				return dsUsers;
			}
			//public static ADsUser FindUsersAndGroups(string containerDN) { 
			//  DirectoryEntry[] users = FindUsers(containerDN);
			//  ADsUser dsUsers = new ADsUser();
			//  foreach (DirectoryEntry user in users) {
			//    ADsUser.th_AdsUserRow rowUser = dsUsers.th_AdsUser.Addth_AdsUserRow(user.Path,user.Name,"","",Props.SAMAccountName(user));
			//    ADsGroupDS dsGroups = GetUserGroupsDS(user);
			//    foreach (ADsGroupDS.t_GroupRow rowGroup in dsGroups.t_Group)
			//      dsUsers.AdsGroup.AddAdsGroupRow(rowUser, rowGroup.sam, rowGroup.cn, rowGroup.dn,rowGroup.sid);
			//  }
			//  return dsUsers;
			//}
			//public static ADsUser FindUsersInGroups(string groupsContainerDN) {
			//  ArrayListDE alUsers = new ArrayListDE();
			//  ADsUser dsUser = new ADsUser();
			//  DirectoryEntry[] groups = FindGroups(groupsContainerDN);
			//  foreach (DirectoryEntry group in groups)
			//    GetGroupUsersDS(group,alUsers,dsUser);
			//  return dsUser;
			//}
			//public static void GetGroupUsersDS(DirectoryEntry deGroup, ArrayListDE alGroups, ADsUser dsUsers) {
			//  if (alGroups.Contains(deGroup.Path)) return;
			//  alGroups.Add(deGroup);
			//  foreach (string memberPath in deGroup.Properties["member"]) {
			//    if (alGroups.Contains("LDAP://" + memberPath)) continue;
			//    DirectoryEntry member = new DirectoryEntry("LDAP://" + memberPath);
			//    if (member.SchemaClassName == "user") {
			//      ADsUser.th_AdsUserRow rowUser = dsUsers.th_AdsUser.FindByUserName(Props.sAM(member));
			//      if (rowUser == null) {
			//        rowUser = dsUsers.th_AdsUser.Addth_AdsUserRow(member.Path, Props.DisplayName(member), "", "", Props.sAM(member));
			//        DirectoryEntry dePG = GetUser(Props.sAM(GetUserPrimaryGroup(member)),"group");
			//        dsUsers.AdsGroup.AddAdsGroupRow(rowUser, Props.sAM(dePG), Props.CN(dePG), Props.DN(dePG));
			//      }
			//      if (dsUsers.AdsGroup.FindByUserNamesam(rowUser.UserName, Props.sAM(deGroup)) == null)
			//        dsUsers.AdsGroup.AddAdsGroupRow(rowUser, Props.sAM(deGroup), Props.CN(deGroup), Props.DN(deGroup));
			//    } else {
			//      GetGroupUsersDS(member, alGroups, dsUsers);
			//      foreach (ADsUser.AdsGroupRow rowGroup in dsUsers.AdsGroup.Select("sam='" + Props.sAM(member) + "'"))
			//        if (dsUsers.AdsGroup.FindByUserNamesam(rowGroup.th_AdsUserRow.UserName, Props.sAM(deGroup)) == null)
			//          dsUsers.AdsGroup.AddAdsGroupRow(rowGroup.th_AdsUserRow, Props.sAM(deGroup), Props.CN(deGroup), Props.DN(deGroup));
			//    }
			//  }
			//}
			//public static string[] GetUserGroups_SAM(DirectoryEntry user) {
			//  StringList groups = new StringList();
			//  ADsSIDClass adsSID = new ADsSIDClass();
			//  user.Invoke("GetInfoEx", new object[] { "tokenGroups" }, 0);
			//  object[] ogroups = (object[])user.Invoke("GetEx", "tokenGroups");
			//  string primeGroup = GetUserPrimaryGroupSAM(user);
			//  groups.Add(primeGroup);
			//  foreach (object o in ogroups) {
			//    adsSID.SetAs((int)ADS_SID_FORMAT.ADS_SID_RAW, o);
			//    string sAM = UserName(adsSID.GetAs((int)ADS_SID_FORMAT.ADS_SID_SAM).ToString());
			//    if (!groups.Contains(sAM)) groups.Add(sAM);
			//  }
			//  return groups;
			//}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Gets a user groups. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="user">					Name of the user. </param>
			/// <param name="regExfilter">	The register exfilter. </param>
			///
			/// <returns>	The user groups. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static string[] GetUserGroups(DirectoryEntry user, string regExfilter) {
				return GroupsDSToSAMList(GetUserGroupsDS(user).t_Group,regExfilter);
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Groups ds to sam list. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="dtGroups">			Groups the dt belongs to. </param>
			/// <param name="regExfilter">	The register exfilter. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			private static string[] GroupsDSToSAMList(ADsGroupDS.t_GroupDataTable dtGroups, string regExfilter) {
				List<string> lstSAMs = new List<string>();
				foreach (ADsGroupDS.t_GroupRow drGroup in dtGroups)
					if ((regExfilter + "") == "" || Regex.IsMatch(drGroup.sam, regExfilter, RegexOptions.IgnoreCase))
						lstSAMs.Add(drGroup.sam);
				return lstSAMs.ToArray();
			}
//        ArrayList groups = new ArrayList();
//        try {
//          byte[] objectSid = user.Properties["objectSid"].Value as byte[];
//          /*


//2. Retrieve the user's Primary Group ID RID 

////calculate the primaryGroupId
//user.RefreshCache(new string[]{"primaryGroupId"});


//3. Overwrite the user's RID on their SID with the Primary Group RID:

//        private byte[] CreatePrimaryGroupSID(byte[] userSid, int primaryGroupID)
//        {
//            //convert the int into a byte array
//            byte[] rid = BitConverter.GetBytes(primaryGroupID);

//            //place the bytes into the user's SID byte array
//            //overwriting them as necessary
//            for (int i=0; i < rid.Length; i++)
//            {
//                userSid.SetValue(rid[i], new long[]{userSid.Length - (rid.Length - i)});
//            }

//            return userSid;
//        }


//4. Finally, construct a binding string from the returned byte[] array and bind 
//adPath = String.Format("LDAP://<SID={0}>", BuildOctetString(sidBytes));
//DirectoryEntry de = new DirectoryEntry(adPath, null, null, AuthenticationTypes.Secure);



//        private string BuildOctetString(byte[] bytes)
//        {
//            StringBuilder sb = new StringBuilder();

//            for(int i=0; i < bytes.Length; i++)
//            {
//                sb.Append(bytes[i].ToString("X2"));
//            }
//            return sb.ToString();
//        }


//          */


//          ADsSIDClass adsSID = new ADsSIDClass();
//          user.Invoke("GetInfoEx", new object[] { "tokenGroups" }, 0);
//          object[] ogroups = (object[])user.Invoke("GetEx", "tokenGroups");
//          string primeGroup = GetUserPrimaryGroupSAM(user);
//          if ((filter == null || Regex.IsMatch(primeGroup, filter, RegexOptions.IgnoreCase)))
//            groups.Add(primeGroup);
//          foreach (object o in ogroups) {
//            adsSID.SetAs((int)ADS_SID_FORMAT.ADS_SID_RAW, o);
//            string sGroup = adsSID.GetAs((int)ADS_SID_FORMAT.ADS_SID_SAM).ToString();
//            sGroup = sGroup.Split('\\')[sGroup.Split('\\').Length - 1];
//            if ((filter == null || Regex.IsMatch(sGroup, filter, RegexOptions.IgnoreCase)) &&
//              !groups.Contains(sGroup))
//              groups.Add(sGroup);
//          }
//          //				if( filter != null )
//          //					foreach( object o in groups )
//          //						if( !Regex.IsMatch((string)o,filter,RegexOptions.IgnoreCase) )
//          //							groups.Remove(o);
//        } catch (System.Runtime.InteropServices.COMException exp) {
//          if (exp.ErrorCode == -2147023541)//No domain found
//            groups.Add("Dealers_WWW_Sales");
//          else throw;
//        }
//        return (string[])groups.ToArray(typeof(System.String));
//      }

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Gets a group users. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <exception cref="Exception">	Thrown when exception. </exception>
			///
			/// <param name="groupShDN">	group RDN. </param>
			///
			/// <returns>	The group users. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry[] GetGroupUsers(string groupShDN) {
				try {
					return GetGroupUsers(new DirectoryEntry(GetFullShDN(groupShDN)));
				} catch (Exception exc) { throw new Exception("DN:" + GetFullShDN(groupShDN), exc); }
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Gets a group users. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="deGroup">	Group the de belongs to. </param>
			///
			/// <returns>	The group users. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry[] GetGroupUsers(DirectoryEntry deGroup) {
				ArrayListDE alUsers = new ArrayListDE();
				GetGroupUsers(deGroup, alUsers);
				List<DirectoryEntry> lst = new List<DirectoryEntry>();
				foreach (DirectoryEntry de in alUsers)
					if (de.SchemaClassName == "user") lst.Add(de);
				return lst.ToArray();
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Gets a group users. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="deGroup">	Group the de belongs to. </param>
			/// <param name="alUsers">	The al users. </param>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static void GetGroupUsers(DirectoryEntry deGroup, ArrayListDE alUsers) {
				if (alUsers.Contains(deGroup.Path)) return;
				alUsers.Add(deGroup);
					try {
						//IADsGroup group = deGroup.NativeObject as IADsGroup;
						//if (group != null) {
						//  IADsMembers users = group.Members();
						//  //users.Filter = "user";
						//  foreach (IADs member in users) {
						//    string path = member.ADsPath;
						//    if (member.Class == "user") alUsers.Add(new DirectoryEntry(member));
						//    else if( !alUsers.Contains(path) )
						//      GetGroupUsers(new DirectoryEntry(member),alUsers);
						//  }
						//  return;
						//}
						foreach (string memberPath in deGroup.Properties["member"]) {
							if (alUsers.Contains("LDAP://" + memberPath)) continue;
							DirectoryEntry member = new DirectoryEntry("LDAP://" + memberPath);
							if (member.SchemaClassName == "user") alUsers.Add(member);
							else GetGroupUsers(member,alUsers);
						}
						//try {
						//  DirectoryEntry[] dePrimeUsers = FindUsersByProperty("primaryGroupID", GetRID(deGroup));
						//  foreach (DirectoryEntry deUser in dePrimeUsers)
						//    alUsers.Add(deUser);
						//} catch {
						//  //throw new RIDException(deGroup.Path, exc);
						//}
						return;
					} catch (Exception exc) {
						try { exc.Data["Path"] = deGroup.Path; } catch { }
						try { exc.Data["Class"] = ((IADs)deGroup.NativeObject).Class; } catch { }
						throw;
					}
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	List group users. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="groupDN">	The group dn. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static ADsUser ListGroupUsers(string groupDN) {
				return ListGroupUsers(groupDN, null);
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	List group users. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <exception cref="ClientException">	Thrown when client. </exception>
			///
			/// <param name="groupDN">	The group dn. </param>
			/// <param name="dsUser">		The ds user. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static ADsUser ListGroupUsers(string groupDN, ADsUser dsUser) {
				string fullDN = GetFullShDN(groupDN);
				try {
					return ListGroupUsers(new DirectoryEntry(fullDN), dsUser);
				} catch (Exception exc) { throw new ClientException(exc, "DN", fullDN); }
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	List group users. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="group">	The group. </param>
			/// <param name="dsUser">	The ds user. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static ADsUser ListGroupUsers(DirectoryEntry group, ADsUser dsUser) {
				DirectoryEntry[] users = GetGroupUsers(group);
				if (dsUser == null) dsUser = new ADsUser();
				foreach (DirectoryEntry user in users) {
					string sAM = Props.SAMAccountName(user);
					if (dsUser.th_AdsUser.Select("UserName='" + sAM + "'").Length == 0)
						dsUser.th_AdsUser.Addth_AdsUserRow(user.Properties["DisplayName"].Value + " Ext:" + TelephoneExtention(user), user.Properties["DisplayName"].Value + "", Telephone(user), TelephoneExtention(user), sAM);
				}
				dsUser.Namespace = "";
				return dsUser;
			}
			/// <summary>
			/// Adds member to group
			/// </summary>
			/// <param name="user">User Account object</param>
			/// <param name="groupShDN">group RDN</param>
			/// <param name="makePrimary">if true - group will be set as primary group for this account</param>
			/// <returns>group object</returns>
			public static DirectoryEntry AddMemberToGroup(DirectoryEntry user, string groupShDN, bool makePrimary) {
				try {
					groupShDN = GetFullShDN(groupShDN);
          //string[] groups = GetUserGroups(user, "");
					DirectoryEntry group = new DirectoryEntry(groupShDN);
					//groups.Username = "afc-home\\webroot"; groups.Password = "cordassian";
					PropertyValueCollection props = group.Properties["member"];
          string userDN = user.Properties[DistinguishedName].Value + "";
          if (!IsMemberOf(user, group)) {
            props.Add(userDN);
            group.CommitChanges();
          }
					if (makePrimary) MakeGroupPrimary(user, group);
					Props.MemberOfCache.Remove(user.Path);
					return group;
				} catch (Exception exc) { throw new Exception("GroupDN:" + groupShDN, exc); }
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Makes a group primary. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="user">		Name of the user. </param>
			/// <param name="group">	The group. </param>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			private static void MakeGroupPrimary(DirectoryEntry user, DirectoryEntry group) {
				try {
					group.RefreshCache(new string[] { "primaryGroupToken" });
					user.Properties["PrimaryGroupID"].Value = group.Properties["primaryGroupToken"].Value;
					user.CommitChanges();
				} catch { }
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Query if 'deObject' is member of primary group. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <exception cref="Exception">	Thrown when exception. </exception>
			///
			/// <param name="deObject">	The de object. </param>
			/// <param name="deGroup">	Group the de belongs to. </param>
			///
			/// <returns>	true if member of primary group, false if not. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			private static bool IsMemberOfPrimaryGroup(DirectoryEntry deObject, DirectoryEntry deGroup) {
				try {
					deGroup.RefreshCache(new string[] { "primaryGroupToken" });
					return deObject.Properties["PrimaryGroupID"].Value.ToString() == deGroup.Properties["primaryGroupToken"].Value.ToString();
				} catch (Exception exc) {
					exc.Data.Add("deObject", deObject.Path);
					exc.Data.Add("deGroup", deGroup.Path);
					throw exc;
				}
			}

      ////////////////////////////////////////////////////////////////////////////////////////////////////
      /// <summary>	Query if 'deObject' is member of. </summary>
      ///
      /// <remarks>	Dimon, 12/21/2010. </remarks>
      ///
      /// <exception cref="Exception">	Thrown when exception. </exception>
      ///
      /// <param name="deObject">	The de object. </param>
      /// <param name="deGroup">	Group the de belongs to. </param>
      ///
      /// <returns>	true if member of, false if not. </returns>
      ////////////////////////////////////////////////////////////////////////////////////////////////////

      private static bool IsMemberOf(DirectoryEntry deObject, DirectoryEntry deGroup) {
				try {
					deGroup.RefreshCache(new string[] { "primaryGroupToken" });
					if (deObject.Properties["PrimaryGroupID"].Value.ToString() == deGroup.Properties["primaryGroupToken"].Value.ToString()) return true;
					return (bool)deGroup.Invoke("IsMember", new object[] { deObject.Path });
				} catch (Exception exc) {
					exc.Data.Add("deObject", deObject.Path);
					exc.Data.Add("deGroup", deGroup.Path);
					throw exc;
				}

        PropertyValueCollection props = deGroup.Properties["member"];
        string dn = deObject.Properties[DistinguishedName].Value + "";
        if (props.Value is string) { if (props.Value.ToString().ToLower() == dn.ToLower()) return true; }
        else {
          int i = 0;
        }
        return false;
      }

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Remove User from Group. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="user">							. </param>
			/// <param name="groupCommonName">	Group's common name without "CN=". </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry RemoveMemberFromGroup(string user, string groupCommonName) {
				return RemoveMemberFromGroup(FindUsersByProperty(sAMAccountName, user)[0], groupCommonName);
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Remove User from Group. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="user">		. </param>
			/// <param name="group">	The group. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry RemoveMemberFromGroup(string user, DirectoryEntry group) {
				return RemoveMemberFromGroup(FindUsersByProperty(sAMAccountName, user)[0], group);
			}
			/// <summary>
			/// Remove User from Group
			/// </summary>
			/// <param name="user"></param>
			/// <param name="groupCommonName">Group's common name without "CN="</param>
			/// <returns></returns>
			private static DirectoryEntry RemoveMemberFromGroup(DirectoryEntry user, string groupCommonName) {
				return RemoveMemberFromGroup(user, new DirectoryEntry().Children.Find(groupCommonName));
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Remove User from Group. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="user">		. </param>
			/// <param name="group">	The group. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry RemoveMemberFromGroup(DirectoryEntry user, DirectoryEntry group) {
        if (group == null || !IsMemberOf(user, group)) return group;
				//group.Username = user.Username;
				//group.Password = user.Password;
				if (IsMemberOfPrimaryGroup(user, group)) ChangePrimaryGroup(user);
				group.Properties["member"].Remove(user.Properties[DistinguishedName].Value);
				group.CommitChanges();
				Props.MemberOfCache.Remove(user.Path);
				return group;
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Change primary group. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="user">	Name of the user. </param>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			private static void ChangePrimaryGroup(DirectoryEntry user) {
				foreach (string group in user.Properties["MemberOf"]) {
					MakeGroupPrimary(user, new DirectoryEntry("LDAP://" + group));
					return;
				}
			}
			#endregion

			#region OU

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Makes an ou name. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="name">	The name. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			private static string MakeOUName(string name) { return "OU=" + Regex.Replace(name, "OU=", "", RegexOptions.IgnoreCase); }

				#region Find

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Searches for the first o us. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="containerDN">	The container dn. </param>
			///
			/// <returns>	The found o us. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry[] FindOUs(string containerDN) { return FindObject(containerDN, "organizationalUnit"); }

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Searches for the first o us. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="container">	The container. </param>
			///
			/// <returns>	The found o us. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry[] FindOUs(DirectoryEntry container) { return FindObject(container, "organizationalUnit"); }

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Searches for the first o us. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="container">	The container. </param>
			/// <param name="scope">			The scope. </param>
			///
			/// <returns>	The found o us. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry[] FindOUs(DirectoryEntry container, SearchScope scope) { return FindObject(container, "organizationalUnit", scope); }
			#endregion

				#region Get

      ////////////////////////////////////////////////////////////////////////////////////////////////////
      /// <summary>	Gets an ou. </summary>
      ///
      /// <remarks>	Dimon, 12/21/2010. </remarks>
      ///
      /// <param name="pathShDN">	The path sh dn. </param>
      /// <param name="create">		true to create. </param>
      ///
      /// <returns>	The ou. </returns>
      ////////////////////////////////////////////////////////////////////////////////////////////////////

      public static DirectoryEntry GetOU(string pathShDN, bool create) { return GetOU("", pathShDN, create); }

      ////////////////////////////////////////////////////////////////////////////////////////////////////
      /// <summary>	Gets an ou. </summary>
      ///
      /// <remarks>	Dimon, 12/21/2010. </remarks>
      ///
      /// <exception cref="ObjectDoesNotExistException">	Thrown when objectdoesnotexist. </exception>
      ///
      /// <param name="ouName">		Name of the ou. </param>
      /// <param name="pathShDN">	The path sh dn. </param>
      /// <param name="create">		true to create. </param>
      ///
      /// <returns>	The ou. </returns>
      ////////////////////////////////////////////////////////////////////////////////////////////////////

      public static DirectoryEntry GetOU(string ouName, string pathShDN, bool create) {
        pathShDN = GetFullShDN(pathShDN);
        if (!create && !DirectoryEntry.Exists(pathShDN)) throw new ObjectDoesNotExistException(pathShDN);
        DirectoryEntry deParent = DirectoryEntry.Exists(pathShDN)? new DirectoryEntry(pathShDN): CreateOU(pathShDN);
				return ouName == "" ? deParent : GetOU(ouName, deParent, create);
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Gets an ou. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <exception cref="Exception">	Thrown when exception. </exception>
			///
			/// <param name="ouName">		Name of the ou. </param>
			/// <param name="objADAM">	The object a dam. </param>
			/// <param name="create">		true to create. </param>
			///
			/// <returns>	The ou. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry GetOU(string ouName, DirectoryEntry objADAM, bool create) {
				try {
					string ouNameDN = MakeOUName(ouName);
					DirectoryEntry deOU = FindADsObject(objADAM, ouNameDN);
					return deOU != null ? deOU : (create ? CreateOU(ouName, objADAM) : null);
				} catch (Exception exc) { throw new Exception("Path:" + objADAM.Path, exc); }
			}
			#endregion

				#region Create

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Creates an ou. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <exception cref="Exception">	Thrown when exception. </exception>
			///
			/// <param name="pathShDN">	The path sh dn. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry CreateOU(string pathShDN) {
				Match mOUs = Regex.Match(pathShDN, "OU=(.+?),((?:OU|DC)=.+)", RegexOptions.IgnoreCase);
				string path = GetFullShDN(mOUs.Groups[2].Value);
        DirectoryEntry deParent = DirectoryEntry.Exists(path) ? new DirectoryEntry(path) : CreateOU(path);
				try {
					return CreateOU(mOUs.Groups[1].Value, deParent);
				} catch (Exception exc) {
					throw new Exception("pathShDN:" + pathShDN + ",path:" + path, exc);
				}
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Creates an ou. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="ouName">		Name of the ou. </param>
			/// <param name="pathShDN">	The path sh dn. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry CreateOU(string ouName, string pathShDN) {
				return CreateOU(ouName, new DirectoryEntry(GetFullShDN(pathShDN)));
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Creates an ou. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <exception cref="ObjectExistsException">	Thrown when objectexists. </exception>
			///
			/// <param name="ouName">		Name of the ou. </param>
			/// <param name="objADAM">	The object a dam. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry CreateOU(string ouName, DirectoryEntry objADAM) {
				string ouNameDN = MakeOUName(ouName);
				DirectoryEntry deNew = FindADsObject(objADAM, ouNameDN);
				if (deNew != null) throw new ObjectExistsException(ouName, deNew);
				deNew = objADAM.Children.Add(ouNameDN, "OrganizationalUnit");
				deNew.CommitChanges();
				return deNew;
			}
			#endregion
			#endregion

			#region RID

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Gets a rid. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="ShDN">	The sh dn. </param>
			///
			/// <returns>	The rid. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static string GetRID(string ShDN) { return GetRID((new DirectoryEntry()).Children.Find(ShDN)); }

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Gets a rid. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="de">	The de. </param>
			///
			/// <returns>	The rid. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static string GetRID(DirectoryEntry de) {
				ADsSIDClass adsSID = new ADsSIDClass();
				adsSID.SetAs((int)ADS_SID_FORMAT.ADS_SID_ACTIVE_DIRECTORY_PATH, de.Path);
				return Regex.Match(adsSID.GetAs((int)ADS_SID_FORMAT.ADS_SID_SDDL).ToString(), @"[^-]+$").Value;
			}
			#endregion

		}
		#endregion

		#region User/Domain Info

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	User domain. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <exception cref="ApplicationException">	Thrown when application. </exception>
		///
		/// <param name="userName">	Name of the user. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private static string UserDomain(string userName){
			string[] a = userName.Split('\\');
			if( a.Length != 2 )throw new ApplicationException("No domain in user name: ["+userName+"].");
			return a[0];
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	User name. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="userName">	Name of the user. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private static string UserName(string userName){
			string[] a = userName.Split('\\');
			return a[a.Length-1];
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets a user. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="userName">	Name of the user. </param>
		///
		/// <returns>	The user. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static DirectoryEntry GetUser(string userName) {
			return isInDomain(userName) ? LDAP.GetUser(userName,"user") : NT.GetUser(userName);
		}
		#endregion

		#region Find

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Searches for the first parent by name. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <exception cref="COMException">	Thrown when an unrecognised HRESULT is returned from a COM
		/// 																method call. </exception>
		/// <exception cref="Exception">		Thrown when exception. </exception>
		///
		/// <param name="deChild">	The de child. </param>
		/// <param name="name">			The name. </param>
		///
		/// <returns>	The found parent by name. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static DirectoryEntry FindParentByName(DirectoryEntry deChild,string name){
			StringList sl = new StringList();
			try{
				while( deChild != null && deChild.Name.ToLower().Split('=')[1] != name.ToLower() ){
					sl.Add(deChild.Name);
					if (LDAP.domainContext == Props.DN(deChild)) return null;
					deChild = deChild.Parent;
				}
				return deChild;
			}catch(COMException exc){
				if( exc.ErrorCode == -2147016661 )return null;
				throw;
			}catch(Exception exc){throw new Exception(deChild.Path+Environment.NewLine+sl,exc); }
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Searches for the first the ds object. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <exception cref="COMException">	Thrown when an unrecognised HRESULT is returned from a COM
		/// 																method call. </exception>
		///
		/// <param name="deParent">	The de parent. </param>
		/// <param name="shDN">			The sh dn. </param>
		///
		/// <returns>	The found the ds object. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private static DirectoryEntry FindADsObject(DirectoryEntry deParent, string shDN) {
			try {
				return deParent.Children.Find(shDN);
			} catch (COMException exc) {
				if (exc.ErrorCode == -2147016656) return null; // No such object
				throw;
			}
		}
		#endregion

		#region Groups/Users

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets a user groups. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="user">					The user. </param>
		/// <param name="regExfilter">	The register exfilter. </param>
		///
		/// <returns>	The user groups. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static string[] GetUserGroups(DirectoryEntry user, string regExfilter) {
			return isInDomain(user) ? LDAP.GetUserGroups(user, regExfilter) : NT.GetUserGroups(user, regExfilter);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets a group members. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="groupShDN">	The group sh dn. </param>
		///
		/// <returns>	The group members. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static string[] GetGroupMembers(string groupShDN) {
			DirectoryEntry ent = new DirectoryEntry();
			DirectoryEntry grpoup = ent.Children.Find(groupShDN);
			ArrayList al = new ArrayList();
			foreach (string dn in grpoup.Properties["member"])
				al.Add(dn);
			return (string[])al.ToArray(typeof(string));
		}
		#endregion

		#region Props class
		public class Props{

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	List all. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="de">	The de. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static string[] ListAll(DirectoryEntry de){
				StringList props = new StringList();
				foreach( string propName in de.Properties.PropertyNames )
					props.Add(propName+" : "+GetProperties(de,propName));
				return props;
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Gets. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="de">	The de. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			[MethodImplAttribute(MethodImplOptions.NoInlining)]
			public static object Get(DirectoryEntry de){
				return GetProperty(de,Common.GetFuncName(1));
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	C ns. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="de">	The de. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			[MethodImplAttribute(MethodImplOptions.NoInlining)]
			public static string CN(DirectoryEntry de){
				return isInDomain(de) ? de.Name.Split('=')[1].Replace("\\","") : de.Name;}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	C ns. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="dn">	The dn. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static string CN(string dn) { return dn.Split(',')[0].Split('=')[1]; }

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	D ns. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="de">	The de. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static string DN(DirectoryEntry de) { return DistinguishedName(de); }

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Distinguished name. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="de">	The de. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			[MethodImplAttribute(MethodImplOptions.NoInlining)]
			private static string DistinguishedName(DirectoryEntry de){
				return isInDomain(de) ? de.Path.Split('/')[2] : de.Name;}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Displays a name described by de. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="de">	The de. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			[MethodImplAttribute(MethodImplOptions.NoInlining)]
			public static string DisplayName(DirectoryEntry de){
				string s = isInDomain(de) ? Get(de)+"" : GetProperty(de,"FullName")+"";
				return s == "" ? CN(de) : s;
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	A m. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="de">	The de. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static string sAM(DirectoryEntry de) { return SAMAccountName(de); }

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Sam account name. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="de">	The de. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			[MethodImplAttribute(MethodImplOptions.NoInlining)]
			public static string SAMAccountName(DirectoryEntry de){
				return isInDomain(de) ? Get(de)+"" : de.Name;
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Mails. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="de">	The de. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			[MethodImplAttribute(MethodImplOptions.NoInlining)]
			public static string Mail(DirectoryEntry de){
				return isInDomain(de) ? Get(de)+"" : GetProperty(de,"EMailAddress")+"";
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Member of cn. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="user">	The user. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static string[] MemberOf_CN(DirectoryEntry user) {
				List<string> lst = new List<string>();
				foreach (string dn in MemberOf(user))
					lst.Add(dn.Split(',')[0].Split('=')[1]);
				return lst.ToArray();
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Gets a user. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="UserName">	Name of the user. </param>
			///
			/// <returns>	The user. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static DirectoryEntry GetUser(string UserName) {
				string qry = String.Format("(|(sAMAccountName={0})(sAMAccountName={0}$))", UserName);
				DirectorySearcher ds = new DirectorySearcher(qry);
				SearchResult sr = ds.FindOne();
				if (sr == null) new Exception("User " + UserName + " not found...");
				return sr.GetDirectoryEntry();
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Member of dn 2000. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="UserName">	Name of the user. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static ADsGroupDS MemberOf_DN2000(string UserName) { return MemberOf_DN2000(UserName, ""); }

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Member of dn 2000. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="UserName">	Name of the user. </param>
			/// <param name="GroupsDN">	The groups dn. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static ADsGroupDS MemberOf_DN2000(string UserName, string GroupsDN) { return MemberOf_DN2000(GetUser(UserName), GroupsDN); }

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Member of dn 2000. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="User">			The user. </param>
			/// <param name="GroupsDN">	The groups dn. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static ADsGroupDS MemberOf_DN2000(DirectoryEntry User,string GroupsDN) {
					User.RefreshCache(new string[]{"tokenGroups"});
					PropertyValueCollection pvc = User.Properties["tokenGroups"];
					List<string> sids = new List<string>();
					foreach (object pv in pvc)
						sids.Add(Dunnry.DirectoryTools.SidUtils.UnsafeConvertSidToStringSid(pv));
					sids = new List<string>(CrackNames(sids.ToArray(), Dunnry.DirectoryTools.DS_NAME_FORMAT.DS_NT4_ACCOUNT_NAME));
					StringBuilder sb = new StringBuilder("(|");
					foreach (string sid in sids)
						sb.AppendFormat("(sAMAccountName={0})", sid.Split('\\')[1]);
					sb.Append(")");
					ADsGroupDS dsGroups = new ADsGroupDS();
					using (DirectorySearcher ds = new DirectorySearcher(sb.ToString(), new string[] { sAMAccountName, ADs.DistinguishedName,"cn","objectSID" })) {
						using (SearchResultCollection src = ds.FindAll()) {
							foreach (SearchResult sr in src)
								if (GroupsDN == "" || Regex.IsMatch(sr.Properties[ADs.DistinguishedName][0] + "", GroupsDN, RegexOptions.IgnoreCase))
									dsGroups.t_Group.Addt_GroupRow(sr.Properties[sAMAccountName][0] + "", sr.Properties["cn"][0] + "", sr.Properties[ADs.DistinguishedName][0] + "", BuildOctetString((byte[])sr.Properties["objectSID"][0]));
						}
					}
					return dsGroups;
				//string[] sAMs = CrackNames(sids, Dunnry.DirectoryTools.DS_NAME_FORMAT.DS_NT4_ACCOUNT_NAME);
					//string[] DNs = CrackNames(sids, Dunnry.DirectoryTools.DS_NAME_FORMAT.);
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Crack names. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="sids">				The sids. </param>
			/// <param name="nameFormat">	The name format. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			private static string[] CrackNames(string[] sids,Dunnry.DirectoryTools.DS_NAME_FORMAT nameFormat) {
				System.IntPtr authIdentity = IntPtr.Zero;
				System.IntPtr hDS = IntPtr.Zero;
				List<string> lstGroups = new List<string>();
				try {
					//bind to the domain using credentials
					hDS = Dunnry.DirectoryTools.DsUtils.Bind();

					//use the bind to display the names
					Dunnry.DirectoryTools.DS_NAME_RESULT_ITEM[] items = Dunnry.DirectoryTools.DsUtils.CrackNames(
						hDS,
						Dunnry.DirectoryTools.DS_NAME_FLAGS.DS_NAME_NO_FLAGS,
						Dunnry.DirectoryTools.DS_NAME_FORMAT.DS_SID_OR_SID_HISTORY_NAME,
						nameFormat,
						sids
						);

					//show on the listbox
					foreach (Dunnry.DirectoryTools.DS_NAME_RESULT_ITEM nri in items) {
						if (nri.status == 0) {
							lstGroups.Add(nri.pName);
						} else
							lstGroups.Add("Not Resolved");
					}
				} finally {
					//first unbind
					Dunnry.DirectoryTools.DsUtils.UnBind(hDS);

					//then release authIdentity
					Dunnry.DirectoryTools.DsUtils.DsFreePasswordCredentials(authIdentity);
				}
				return lstGroups.ToArray();
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Member of dn. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="UserName">	Name of the user. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			internal static ADsGroupDS MemberOf_DN(string UserName) { return MemberOf_DN(UserName, ""); }

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Member of dn. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="UserName">	Name of the user. </param>
			/// <param name="GroupsDN">	The groups dn. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			internal static ADsGroupDS MemberOf_DN(string UserName, string GroupsDN) { return MemberOf_DN(GetUser(UserName), GroupsDN); }

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Member of dn. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="User">			The user. </param>
			/// <param name="GroupsDN">	The groups dn. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			internal static ADsGroupDS MemberOf_DN(DirectoryEntry User, string GroupsDN) {
				return MemberOf_DN2000(User,GroupsDN);
				//IdentityReferenceCollection irc = ExpandTokenGroups(User).Translate(typeof(NTAccount));
				IdentityReferenceCollection irc = ExpandTokenGroups(User);
				StringBuilder sb = new StringBuilder("(|");
				foreach (SecurityIdentifier account in irc)
					sb.AppendFormat("(objectSid={0})", account.ToString());//In Win2000 search by objectSID returns nothing
				sb.Append(")");
				ADsGroupDS dsGroups = new ADsGroupDS();
				using (DirectorySearcher ds = new DirectorySearcher(sb.ToString(), new string[] { sAMAccountName,ADs.DistinguishedName,"cn","objectSID" })) {
					using (SearchResultCollection src = ds.FindAll()) {
						foreach (SearchResult sr in src)
							dsGroups.t_Group.Addt_GroupRow(sr.Properties[sAMAccountName][0] + "", sr.Properties["cn"][0] + "", sr.Properties[ADs.DistinguishedName][0] + "", BuildOctetString((byte[])sr.Properties["objectSID"][0]));
					}
				}
				return dsGroups;
			}
//Sample Helper Function

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Expand token groups. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="user">	The user. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			private static IdentityReferenceCollection ExpandTokenGroups(DirectoryEntry user) {
				user.RefreshCache(new string[] { "tokenGroups" });
				IdentityReferenceCollection irc = new IdentityReferenceCollection();
				foreach (byte[] sidBytes in user.Properties["tokenGroups"])
					irc.Add(new SecurityIdentifier(sidBytes, 0));
				return irc;
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Member of the given user. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="user">	The user. </param>
			///
			/// <returns>	. </returns>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static string[] MemberOf(DirectoryEntry user) {
				if (!isInDomain(user)) return NT.GetUserGroups(user, null);
				//if (MemberOfCache.Contains(user.Path)) return (string[])MemberOfCache[user.Path];
				List<string> lstGroups = new List<string>();
				MemberOf(user,lstGroups );
				//MemberOfCache.Add(user.Path, lstGroups.ToArray());
				return lstGroups.ToArray();
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>	Member of the given user. </summary>
			///
			/// <remarks>	Dimon, 12/21/2010. </remarks>
			///
			/// <param name="user">		The user. </param>
			/// <param name="groups">	The groups. </param>
			////////////////////////////////////////////////////////////////////////////////////////////////////

			public static void MemberOf(DirectoryEntry user, List<string> groups) {
				if (user.SchemaClassName == "user")
					groups.Add(Props.DistinguishedName(LDAP.GetUser(LDAP.GetUserPrimaryGroupSAM(user), "group")));
				foreach (string group in user.Properties["MemberOf"])
					if (!groups.Contains(group)) {
						groups.Add(group);
						MemberOf( new DirectoryEntry("LDAP://" + group),groups);
					}
			}
			public static Hashtable MemberOfCache = Hashtable.Synchronized(new Hashtable());

		}
		#endregion


		#region Public Helpers
			#region GetFullShDN
		public static string GetFullShDN(string shDN){
			if (shDN == "") return "";
			if( shDN.IndexOf("://") >= 0 )return shDN;
			return isDN(shDN) ? LDAP.GetFullShDN(shDN) : NT.GetFullShDN(shDN);
		}
			#endregion

    ////////////////////////////////////////////////////////////////////////////////////////////////////
    /// <summary>	Build an OctetString suitable for binding. </summary>
    ///
    /// <remarks>	Dimon, 12/21/2010. </remarks>
    ///
    /// <param name="bytes">	. </param>
    ///
    /// <returns>	. </returns>
    ////////////////////////////////////////////////////////////////////////////////////////////////////

    private static string BuildOctetString(byte[] bytes) {
      StringBuilder sb = new StringBuilder();
      for (int i = 0; i < bytes.Length; i++) {
        sb.Append(bytes[i].ToString("X2"));
      }
      return sb.ToString();
    }

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets a ds path array. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="adsPath">	Full pathname of the ads file. </param>
		///
		/// <returns>	a ds path array. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private static string[] GetADsPathArray(string adsPath){
			return Regex.Replace(adsPath,"\\w{1,2}=","").Split(',');
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Query if 'path' is dn. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="path">	Full pathname of the file. </param>
		///
		/// <returns>	true if dn, false if not. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static bool isDN(string path){
			if( path.StartsWith(WinNT) )return false;
			if( path.ToUpper().StartsWith("LDAP://") )return true;
			if( path.ToUpper().StartsWith("CN=") )return true;
			if( path.ToUpper().StartsWith("OU=") )return true;
			return false;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Query if 'userName' is in domain. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="userName">	Name of the user. </param>
		///
		/// <returns>	true if in domain, false if not. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static bool isInDomain(string userName){
			if( userName.StartsWith(WinNT) )return false;
			if( userName.ToUpper().StartsWith("LDAP://") )return true;
			if( userName.ToUpper().StartsWith("CN=") )return true;
			if( userName.ToUpper().StartsWith("OU=") )return true;
			if( userName.ToUpper().IndexOf("://") >= 0 )return false;
			return Environment.MachineName.ToLower() != UserDomain(userName).ToLower();
			//	ComputerSystem cs = new ComputerSystem(Dns.GetHostName());
			//	return cs.Domain != "WORKGROUP";
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Query if 'userName' is in domain. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="user">	The user. </param>
		///
		/// <returns>	true if in domain, false if not. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static bool isInDomain(DirectoryEntry user){return user.Path.IndexOf("LDAP://") >= 0;}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the properties. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="deObject">	The de object. </param>
		/// <param name="propName">	Name of the property. </param>
		///
		/// <returns>	The properties. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static ArrayListEx GetProperties(DirectoryEntry deObject,string propName){
			if( !deObject.Properties.Contains(propName) )return null;
			ArrayListEx props = new ArrayListEx();
			foreach( object v in deObject.Properties[propName] )
				props.Add(v);
			return props;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets a property. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="deObject">	The de object. </param>
		/// <param name="propName">	Name of the property. </param>
		///
		/// <returns>	The property. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static object GetProperty(DirectoryEntry deObject,string propName){
			if( !deObject.Properties.Contains(propName) || deObject.Properties[propName].Count == 0 )return null;
			return deObject.Properties[propName][0].ToString();
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Telephones. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="deUser">	The de user. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static string Telephone(DirectoryEntry deUser){
			return GetProperty(deUser,isInDomain(deUser.Path)?"homePhone":"telephoneNumber")+"";
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Telephone extention. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="user">	The user. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static string TelephoneExtention(string user){return TelephoneExtention(GetUser(user));}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Telephone extention. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="deUser">	The de user. </param>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static string TelephoneExtention(DirectoryEntry deUser){
			return GetProperty(deUser,isInDomain(deUser.Path)?"otherHomePhone":"otherTelephone")+"";
		}
		#endregion
	}

	#region ArrayListDE

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>	Array list de. </summary>
	///
	/// <remarks>	Dimon, 12/21/2010. </remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////

	public class ArrayListDE : ArrayList{

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Default constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public ArrayListDE():base(){}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="collection">	The collection. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public ArrayListDE(ICollection collection):base(collection){}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Indexer to get items within this collection using array index syntax. </summary>
		///
		/// <value>	The indexed item. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public new DirectoryEntry this[int index]{get{ return (DirectoryEntry)base[index];}}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	DirectoryEntry[] casting operator. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="a">	a. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static implicit operator DirectoryEntry[](ArrayListDE a){return (DirectoryEntry[])a.ToArray(typeof(DirectoryEntry));}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Determines if this collection contains a given object. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="path">	The string to test for containment. </param>
		///
		/// <returns>	true if the object is in this collection, false if not. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public bool Contains(string path) {
			foreach (DirectoryEntry de in this)
				if (de.Path.ToLower() == path.ToLower()) return true;
			return false;
		}
	}
	#endregion
}
